/*  This file is part of the comicconquer project
    Copyright (C) 2010 Reach <reach334@gmail.com>

    ComicConquer is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This software is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public 
    License along with this software.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "PageStruct.h"
#include <math.h>

#define PAGEQUEUE_LEN   7   // length of the page queue, must be odd
int g_processSequence[] = {
    0, 1, -1, 2, -2, 3, -3
};  // the page process sequence, base on the current page
int g_processSeq_Prev[] = {
    0, -1, 1, -2, 2, -3, 3
};  // the other version of sequence for turning page previous

PageQueue::PageQueue() {
    In_Log(__FUNCTION__);
    m_quLen = PAGEQUEUE_LEN;
    m_quLen_2 = m_quLen>>1;
    m_pageStart = -m_quLen_2; 
    m_pageList = NULL;
    m_reloadRequest = false;
    Out_Log(__FUNCTION__);
}

PageQueue::~PageQueue(){
    In_Log(__FUNCTION__);
    if (m_pageList != NULL)
    {
        delete[] m_pageList; 
        m_pageList = NULL;
    }
    Out_Log(__FUNCTION__);
}

void PageQueue::initPageList(){
    In_Log(__FUNCTION__);

    QMutexLocker locker(&m_mutex);

    if (m_pageList != NULL)
    {
        delete[] m_pageList;
        PageQueue();
    }

    m_pageList = new Page[m_nPage + m_quLen-1];

    for (int i = 0; i < m_nPage + m_quLen-1; i++)
    {
        m_pageList[i].iPage = i - m_quLen_2; 
        m_pageList[i].pageCount = m_nPage;
    }
    m_pageStart = -m_quLen_2; 
    m_pageLast = -1; // let the updateQueue() update the "needed" flags

    Out_Log(__FUNCTION__);
}

Page* PageQueue::getPage(int iPage){
    In_Log(__FUNCTION__);

    QMutexLocker locker(&m_mutex);

    int index = iPage - m_pageStart;
    Page* page = NULL;

    if (index >= 0 && index < m_quLen)
    {
        page = &m_pageList[m_quLen_2 + iPage];
    }

    Out_Log(__FUNCTION__);
    return page;
}

Page* PageQueue::getUnprocess(bool isPrev){
    In_Log(__FUNCTION__);

    QMutexLocker locker(&m_mutex);

    if (m_iProcessed >= m_quLen)
    {
        Out_Log(__FUNCTION__);
        return NULL;
    }

    Page* page = NULL;
    int* seq = g_processSequence;
    if (isPrev)
    {
        seq = g_processSeq_Prev;
    }

    do
    {
        page = &m_pageList[m_quLen_2 + m_pageCur + seq[m_iProcessed++]];
    }while ((page->iPage < 0 || page->iPage >= m_nPage) && m_iProcessed < m_quLen);

    // this page need not process
    if (page->iPage < 0 || page->iPage >= m_nPage)
    {
        page = NULL; 
    }

    Out_Log(__FUNCTION__);
    return page;
}

void PageQueue::moveTo(int iPage){
    In_Log(__FUNCTION__);

    QMutexLocker locker(&m_mutex);

    m_pageCur = iPage;
    m_iProcessed = 0;

    Out_Log(__FUNCTION__);
}

// Update the queue according to the flags m_initRequest and m_reloadRequest, and the m_pageCur compared with the m_pageLast.
void PageQueue::updateQueue()
{
    if (m_initRequest)
    {
        m_initRequest = false;
        initPageList();
    }

    if (m_pageCur != m_pageLast)
    {
        // release unneeded pages
        int nRelease = abs(m_pageCur - m_pageLast) < m_quLen ? abs(m_pageCur - m_pageLast): m_quLen;
        int start = m_pageCur > m_pageLast ? m_pageStart : m_pageStart + m_quLen - nRelease;
        for (int i = 0; i < nRelease; i++)
        {
            m_pageList[m_quLen_2 + start + i].release();
        }

        m_pageLast = m_pageCur;
        m_pageStart = m_pageCur - m_quLen_2;

        // update the "needed" flags
        for (int i = 0; i < m_quLen; i++)
        {
            m_pageList[m_quLen_2 + m_pageStart + i].needed = true; 
        }
    }

    if (m_reloadRequest)
    {
        m_reloadRequest = false;

        for (int i = 0; i < m_quLen; i++)
        {
            m_pageList[m_quLen_2 + m_pageStart + i].release();
            m_pageList[m_quLen_2 + m_pageStart + i].needed = true;
        }
    }
}

void PageQueue::requestReload()
{
    QMutexLocker locker(&m_mutex);
    m_reloadRequest = true;
}

bool PageQueue::isReloadRequest()
{
    QMutexLocker locke(&m_mutex);
    return m_reloadRequest;
}

void PageQueue::requestInit(int nPage)
{
    QMutexLocker locker(&m_mutex);
    m_nPage = nPage;
    m_initRequest = true;
}

bool PageQueue::isInitRequest()
{
    QMutexLocker locke(&m_mutex);
    return m_initRequest;
}

void PageQueue::lock()
{
    m_mutex.lock();
}

void PageQueue::unlock()
{
    m_mutex.unlock();
}
